/*
	Copyright (c) 2010 <copyright holders>
	This source code is licensed under The MIT License,
	see the file LICENSE for details.
	*/

#ifndef UNIT_HPP
#define UNIT_HPP

#include <boost/enable_shared_from_this.hpp>
#include <OGRE/OgrePrerequisites.h>
#include <string>

#include "ForwardDecls.hpp"
#include "HelpDefs.hpp"
#include "Spatial.hpp"

namespace spelprog2 {
namespace sim {

enum UnitTaskState {
    TASK_IDLE = 0,          //looking for new fight/flee task somewhat periodically, does nothing else
    TASK_MOVE_TO_TARGET,    //given by Squad: moving towards target position, then rotating to target facing after stopping
    TASK_FIGHT,             //fighting with some enemy (only does this while in range to find more enemies)
    TASK_HARRASS,           //use ranged weapons to rape enemy
    TASK_FLEE,              //runs away to wherever (hopefully from enemy)
    TASK_DEAD,              //doesn't respond to orders anymore for rather natural causes
    TASK_COUNT
};

/*  Explanation of possible task state transitions:
    { any other state } => TASK_DEAD, TASK_DEAD => { }: If unit dies (health = 0), it is irreversibly dead.
    TASK_IDLE => { any other state }: Unit can automatically go into TASK_FIGHT or TASK_FLEE (or TASK_DEAD).
        Other states can be entered through squad.
    TASK_MOVE_TO_TARGET => { TASK_IDLE, TASK_FIGHT, TASK_FLEE, TASK_DEAD }:
        Unit will continue performing this task until successful termination (TASK_IDLE), or interrupted by a
        fight or a panic.
    TASK_FIGHT => { TASK_FLEE, TASK_IDLE, TASK_DEAD }: Unit fights until it kills enemy (idle), panics, or dies.
    TASK_HARRASS => -/- (same as TASK_FIGHT): Unit uses long range weaponry until it kills enemy (idle), panics, or dies.
    TASK_FLEE => { TASK_IDLE, TASK_DEAD }: Unit panics until it reforms (morale reaching certain threadhold),
        or dies (or leaves map).

    Usage of persist timer for various states:
    TASK_FIGHT: Determines how long target should be targeted without any damage being dealt.
    TASK_IDLE: Used to check for far targets with a certain interval (expensive operation).
    */

struct Unit : public boost::enable_shared_from_this<Unit>
{
	typedef boost::shared_ptr<Unit> Pointer;
    typedef boost::shared_ptr<const Unit> ConstPointer;
    typedef boost::weak_ptr<Unit> WeakPointer;

    typedef boost::weak_ptr<Squad> SquadPtr;
    typedef boost::weak_ptr<const Squad> ConstSquadPtr;
private:
    typedef std::list<Unit::Pointer> UnitList;

    HIDE_EVIL_CONSTRUCTORS(Unit);

    Unit(const UnitInvariant & invariant, Int playerId, Squad & squad, World & world, const Int2d & position, Int facing);

    World & world_;
    const UnitInvariant & invariant_;
    Unit::WeakPointer targetUnit_;
    UnitTaskState task_;
    SquadPtr squad_;
    Int2d   target_;
    Int2d   position_;
    Int     facing_;
    Int     targetFacing_;
    Int     health_;
    Int     morale_;
    Int     team_;
    Int     attackTimer_;
    Int     persistTimer_; //see state description for usage

    static Int limitAngle(Int a);
    static Int calcUnsignedAngularDifference(Int a, Int b);
    static Int calcSignedAngularDifference(Int a, Int b);
    
    Int calcDesiredFacing(const Int2d & forceSum) const;
    void calcMeleeDamage(Int & healthDamage, Int & moraleDamage, const Unit & target, Int optimalAttackHeading);
    void calcBaseDamage(Int & healthDamage, Int & moraleDamage);

    void lookForFight(const UnitList & units, UnitTaskState type);
    void lookForHarrass();
    void checkForPanic();

    void avoidFriends(Int2d & forceSum, const UnitList & nearUnits);
    void updatePosition(const Int2d & forceSum);
    void updateFacing(const Int2d & forceSum);
    void moveToTarget(Int2d & forceSum);
    void fight(Int2d & forceSum);
    void harrass();
    void flee(Int2d & forceSum);
public:
    static Unit::Pointer create(const UnitInvariant & invariant, Int playerId, Squad & squad,
        World & world, const Int2d & position, Int facing);
    ~Unit();

    SquadPtr getSquad()                 { return squad_; }
    ConstSquadPtr getSquad() const      { return squad_; }

    Unit::Pointer getTargetUnit()       { return targetUnit_.lock(); }
    const Int2d & getPosition() const   { return position_; }
    Int getFacing() const               { return facing_; }
    const UnitInvariant & getInvariant() const { return invariant_; }

    void simUpdate(Int sequenceNbr);
    void setTarget(const Int2d & position, Int facing);
    void setTarget(Unit::Pointer enemy);
    void setTeam(Int team);
    void damage(Int health, Int morale);

    bool readyForNewTarget() const { return task_ == TASK_IDLE || task_ == TASK_MOVE_TO_TARGET || task_ == TASK_HARRASS; }
    bool isActive() const { return readyForNewTarget() || task_ == TASK_FIGHT; }

    bool isDead() const             { return task_ == TASK_DEAD; }
    bool isIdle() const             { return task_ == TASK_IDLE; }
    bool isFighting() const         { return task_ == TASK_FIGHT; }
    bool isHarrassing() const       { return task_ == TASK_HARRASS; }
    bool isFleeing() const          { return task_ == TASK_FLEE; }

    bool startedAttack() const;
};


}; //namespace sim
}; //namespace spelprog2

#endif